#import "WWSimpleImageView.h"
#import "aspectRatios.h"

@implementation WWSimpleImageView


#define WW_BOTTOMALIGNED  0
#define WW_TOPALIGNED     1


+ initialize { return [WWSimpleImageView setVersion:6], self; }

- initFrame:(const NXRect *)r 
{
   [super initFrame:r];
   backgroundColor = NXConvertRGBToColor(0.96, 1.0, 0.6);
   borderType = 0;
   alpha = 1.0;
   image = nil;
   aspectRatioType = WW_ASPECT_DONT_CARE;
   aspectRatio = 1.0;
   scaleToFit = NO;
   imageUnder = NO;
   horizontalLayoutType = NX_LEFTALIGNED;
   verticalLayoutType = WW_BOTTOMALIGNED;

   return self;
}

- awake
{
   [super awake];
   return self;
}


- free
{
  if (controlPanel)
  {  [controlPanel setFreeWhenClosed:NO];
     [controlPanel close];
     [controlPanel free];
     controlPanel = nil;
  }
  return [super free];
}
#define EPSILON 10

- sizeTo:(NXCoord)width :(NXCoord)height
{
  // don't want to make it too small...
  if (width < EPSILON) return nil;
  if (height < EPSILON) return nil;


  // let's say it comes in at 100, 100 and you're supposed to be constrained to 4:3
  // you don't want it to get bigger than 100, 100, so... you frob the width, not the height
  switch (aspectRatioType)
  {  case WW_ASPECT_DONT_CARE:
            aspectRatio = width/height;
            break;
     case WW_ASPECT_NTSC:  // 1.33:1
            height = width/1.33;
            aspectRatio = 1.33;
            break;
     case WW_ASPECT_AMERICAN_WIDESCREEN:  // 1.85:1
            //width = height * 1.85;
            height = width/1.85;
            aspectRatio = 1.85;
            break;
     case WW_ASPECT_EUROPEAN_WIDESCREEN:  // 1.66:1
            //width = height * 1.66;
            height = width/1.66;
            aspectRatio = 1.66;
            break;
     case WW_ASPECT_VISTA_VISION:  // 2.21:1
            //width = height * 1.21;
            height = width/2.21;
            aspectRatio = 2.21;
            break;
     case WW_ASPECT_SQUARE:  // 1:1
            if (width < height)
	    {  height = width;
	    }
	    else
	    {  width = height;
	    }
            aspectRatio = 1.0;
            break;
     case WW_ASPECT_CUSTOM:  //aspectX:aspectY
            if (aspectRatio < 1.0)
	    {  width = height * aspectRatio;
	    }
	    else
	    {  height = width/aspectRatio;
	    }
            break;
     default:
            NXLogError("unknown aspect ratio type: %d", aspectRatioType);
            break;
  }

  [super sizeTo:width :height];
  [self display];

  return self;
}

- drawSelf:(const NXRect *)theRect :(int)n 
{
  NXPoint  p = {0.0, 0.0};
  NXSize   imageSize;


  if (clearTheView)  
  {  NXSetColor(backgroundColor);
     PSsetalpha(0.0);
     PScompositerect(theRect->origin.x, theRect->origin.y, 
		     theRect->size.width, theRect->size.height,
		     NX_SOVER);
     return self;
  }

  [image getSize:&imageSize];
  switch (verticalLayoutType)
  {  case WW_BOTTOMALIGNED:
          p.y = 0.0;
	  break;
     case WW_TOPALIGNED:
          p.y = theRect->size.height - imageSize.height;
 	  break;
    default:
          p.y = 0.0;
	  break;
  }
  switch (horizontalLayoutType)
  {  case NX_LEFTALIGNED:
          p.x = 0.0;
	  break;
     case NX_RIGHTALIGNED:
          p.x = theRect->size.width - imageSize.width;
 	  break;
     case NX_CENTERED:
          if (theRect->size.width > imageSize.width)
          {  p.x = (theRect->size.width - imageSize.width)/2.0;
          }
          else
          {  p.x = 0.0;
          }
          if (theRect->size.height > imageSize.height)
          {  p.y = (theRect->size.height - imageSize.height)/2.0;
          }
          else
          {  p.y = 0.0;
          }
	  break;
    default:
          p.x = 0.0;
	  break;
  }

  if (imageUnder)
  {  if (scaleToFit)
     {  [[image bestRepresentation] drawIn:(const NXRect *)&bounds];
     }
     else
     {  [image composite:NX_SOVER toPoint:&p];
     }
     NXSetColor(backgroundColor);
     PSsetalpha(alpha);
     PScompositerect(theRect->origin.x, theRect->origin.y, 
		     theRect->size.width, theRect->size.height,
		     NX_SOVER);
  }
  else
  {  NXSetColor(backgroundColor);
     PSsetalpha(alpha);
     PScompositerect(theRect->origin.x, theRect->origin.y, 
		     theRect->size.width, theRect->size.height,
		     NX_SOVER);
     if (scaleToFit)
     {  [[image bestRepresentation] drawIn:(const NXRect *)&bounds];
     }
     else
     {  [image composite:NX_SOVER toPoint:&p];
     }
  }  

  if (borderType)
  {  NXRect  r = *theRect;


     PSsetalpha(1.0);

     r.origin.x++; r.origin.y--;        
     NXSetColor(NX_COLORBLACK); NXFrameRect(&r);

     r.origin.x--, r.origin.y++;        
     NXSetColor(NX_COLORGRAY); NXFrameRect(&r);

     r.origin.x--; r.size.width++; r.size.height++; 
     NXSetColor(NX_COLORWHITE); NXFrameRect(&r);
  }

  return self;
}

- setBorderType:(int)newBorderType { borderType = newBorderType; return self; }
- (int)borderType { return borderType; }

//
- sizeToImage:sender 
{
   NXSize s;


    if (image)
    {  [image getSize:&s];
       [self sizeTo:s.width :s.height];
    }
    return self;
}

- setScaleToFit:(BOOL)flag { scaleToFit = flag; return self; }
- (BOOL)scaleToFit { return scaleToFit; }
- setImageUnder:(BOOL)flag { imageUnder = flag; return self; }
- (BOOL)imageUnder { return imageUnder; }

- (int)aspectRatioType { return aspectRatioType; }
- (float)aspectRatio { return aspectRatio; }
- setAspectRatioFromMatrix:sender
{
  NXRect  newRect;


  [self getFrame:&newRect];
  aspectRatioType = [[sender selectedCell] tag];
  switch (aspectRatioType)
  {  case WW_ASPECT_DONT_CARE:
            break;
     case WW_ASPECT_NTSC:  // 1.33:1
            newRect.size.height = newRect.size.width/1.33;
            break;
     case WW_ASPECT_AMERICAN_WIDESCREEN:  // 1.85:1
            newRect.size.height = newRect.size.width/1.85;
            break;
     case WW_ASPECT_EUROPEAN_WIDESCREEN:  // 1.66:1
            newRect.size.height = newRect.size.width/1.66;
            break;
     case WW_ASPECT_VISTA_VISION:  // 2.21:1
            newRect.size.height = newRect.size.width/2.21;
            break;
     case WW_ASPECT_SQUARE:  // 1:1
            if (newRect.size.width < newRect.size.height)
            {  newRect.size.height = newRect.size.width;
	    }
	    else
	    {  newRect.size.width = newRect.size.height;
	    }
            break;
     case WW_ASPECT_CUSTOM:  //aspectX:aspectY
            if (aspectRatio < 1.0)
	    {  newRect.size.width = newRect.size.height * aspectRatio;
	    }
	    else
	    {  newRect.size.height = newRect.size.width/aspectRatio;
	    }
            break;
     default:
            NXLogError("unknown aspect ratio type: %d", aspectRatioType);
            break;
  }
  [self sizeTo:newRect.size.width :newRect.size.height];

  return self;
}
- setAspectRatioType:(int)newAspectRatioType
{
  NXRect  newRect;


  [self getFrame:&newRect];
  aspectRatioType = newAspectRatioType;
  switch (aspectRatioType)
  {  case WW_ASPECT_DONT_CARE:
            break;
     case WW_ASPECT_NTSC:  // 1.33:1
            newRect.size.height = newRect.size.width/1.33;
            break;
     case WW_ASPECT_AMERICAN_WIDESCREEN:  // 1.85:1
            newRect.size.height = newRect.size.width/1.85;
            break;
     case WW_ASPECT_EUROPEAN_WIDESCREEN:  // 1.66:1
            newRect.size.height = newRect.size.width/1.66;
            break;
     case WW_ASPECT_VISTA_VISION:  // 2.21:1
            newRect.size.height = newRect.size.width/2.21;
            break;
     case WW_ASPECT_SQUARE:  // 1:1
            if (newRect.size.width < newRect.size.height)
            {  newRect.size.height = newRect.size.width;
	    }
	    else
	    {  newRect.size.width = newRect.size.height;
	    }
            break;
     case WW_ASPECT_CUSTOM:  //aspectX:aspectY
            if (aspectRatio < 1.0)
	    {  newRect.size.width = newRect.size.height * aspectRatio;
	    }
	    else
	    {  newRect.size.height = newRect.size.width/aspectRatio;
	    }
            break;
     default:
            NXLogError("unknown aspect ratio type: %d", aspectRatioType);
            break;
  }
  [self sizeTo:newRect.size.width :newRect.size.height];

  return self;
}
//
- setAspectRatio:sender
{
  NXRect  newRect;


  [self getFrame:&newRect];
  aspectRatio = [sender floatValue];

  if (aspectRatioType != WW_ASPECT_CUSTOM)
  {  return self;
  }
  if (aspectRatio < 1.0)
  {  newRect.size.width = newRect.size.height * aspectRatio;
  }
  else
  {  newRect.size.height = newRect.size.width/aspectRatio;
  }
  [self sizeTo:newRect.size.width :newRect.size.height];

  return self;
}
//
- setHorizontalLayoutFromMatrix:sender  {  horizontalLayoutType = [[sender selectedCell] tag];  return self; }
- (int)horizontalLayoutType { return horizontalLayoutType; }

- setVerticalLayoutFromMatrix:sender  {  verticalLayoutType = [[sender selectedCell] tag];  return self; }
- (int)verticalLayoutType { return verticalLayoutType; }


- (NXColor) backgroundColor 	{ return backgroundColor; }
- setBackgroundColor:(NXColor)c { return backgroundColor = c, self; }
- (float) alpha			{ return alpha; }
- setBackgroundAlpha:(float)n	{ return alpha = n, self; }

- saveImage:sender
{
  static id savePanel=nil;
  NXStream *ts;


  if (!savePanel) 
  {  savePanel=[SavePanel new];
  }

  [savePanel setRequiredFileType:"tiff"];
  if([savePanel runModal])
  {  ts = NXOpenMemory(NULL, 0, NX_WRITEONLY);
     [image writeTIFF:ts allRepresentations:NO usingCompression:NX_TIFF_COMPRESSION_LZW andFactor:1.0];
     NXSaveToFile(ts, [savePanel filename]);
     NXCloseMemory(ts,NX_FREEBUFFER);
  }

  return self;
}

- setImageFile:(const char *)filename 
{
    // It's actually a bad idea to free this image, because it might be shared...
    if (filename && *filename)
    { image = [NXImage findImageNamed:filename];
      if (!image) 
      {  image = [[NXImage alloc] init];
         [image setDataRetained:YES];
         if (![image loadFromFile:filename])
         {  NXLogError("unable to load image from file <%s>\n", filename);
            return nil; 
 	 }
      }
    }
    clearTheView = YES;
    [self display];
    clearTheView = NO;
    [window display];
    return self;
}

- setImage:i  
{  image = i; 
   clearTheView = YES;
   [self display];
   clearTheView = NO;
   [window display];
   return self; 
}
- image { return image; }

- (const char *)getInspectorClassName { return "WWSimpleImageViewIBInspector"; }

- revertControlPanel:sender 
{ 
   [theColor setColor:[self backgroundColor]];
   [alphaSlider setFloatValue:[self alpha]];
   [horizontalLayoutMatrix selectCellWithTag:[self horizontalLayoutType]];
   [verticalLayoutMatrix selectCellWithTag:[self verticalLayoutType]];
   [borderTypeMatrix selectCellWithTag:[self borderType]];
   clearTheView = YES; [self display];
   clearTheView = NO;  [window display]; NXPing();

   return self;
}

- loadControlPanel
{
   char buf[MAXPATHLEN + 1];
   id bundle;

   
   // we want to support two different things here.  
   // 1st scenario: lots of MovieViews, screen space is expensive, we want to have these control panels 
    
   if (!controlPanel)
   {  bundle = [NXBundle bundleForClass:[self class]];
      [bundle getPath:buf forResource:"WWSimpleImageViewControlPanel" ofType:"nib"];
      [NXApp loadNibFile:buf owner:self withNames:NO fromZone:[self zone]];
      if (!controlPanel)
      {  NXLogError("problem loading WWSimpleMovieViewControlPanel\n");
         return nil;
      }
      [controlPanel setDelegate:self];
   }
   [self revertControlPanel:nil];
   [controlPanel makeKeyAndOrderFront:self];
   return self;
}


- mouseDown:(NXEvent *)theEvent
{
  if (theEvent->flags & NX_COMMANDMASK)  // show the control panel
  {  [self loadControlPanel];
     return self;
  }
  return self;
}


#define typeVectorVersion1 "@f"
#define typeValuesVersion1 &image, &alpha

#define typeVectorVersion2 "@fif"
#define typeValuesVersion2 &image, &alpha, &aspectRatioType, &aspectRatio

#define typeVectorVersion3 "@fifcc"
#define typeValuesVersion3 &image, &alpha, &aspectRatioType, &aspectRatio, &imageUnder, &scaleToFit

#define typeVectorVersion4 "@fifcci"
#define typeValuesVersion4 &image, &alpha, &aspectRatioType, &aspectRatio, &imageUnder, &scaleToFit, &borderType

#define typeVectorVersion5 "@fifccii"
#define typeValuesVersion5 &image, &alpha, &aspectRatioType, &aspectRatio, &imageUnder, &scaleToFit, &borderType, &horizontalLayoutType

#define typeVector "@fifcciii"
#define typeValues &image, &alpha, &aspectRatioType, &aspectRatio, &imageUnder, &scaleToFit, &borderType, &horizontalLayoutType, &verticalLayoutType

- read:(NXTypedStream*)stream 
{
    int version;
    [super read:stream];


    version = NXTypedStreamClassVersion(stream, "WWSimpleImageView");
    if (version == 0) NXReadTypes(stream, "i", &version), version=1;
    if (version == 1)
    {  NXReadTypes(stream, typeVectorVersion1, typeValuesVersion1);
       backgroundColor = NXReadColor(stream);
       aspectRatioType = WW_ASPECT_SQUARE;
       aspectRatio = 1.0;
       imageUnder = NO;
       scaleToFit = NO;
       borderType = 0;
       horizontalLayoutType = NX_LEFTALIGNED;
       verticalLayoutType = WW_BOTTOMALIGNED;
    } 
    if (version == 2)
    {  NXReadTypes(stream, typeVectorVersion2, typeValuesVersion2);
       backgroundColor = NXReadColor(stream);
       imageUnder = NO;
       scaleToFit = NO;
       borderType = 0;
       horizontalLayoutType = NX_LEFTALIGNED;
       verticalLayoutType = WW_BOTTOMALIGNED;
    }
    if (version == 3)
    {  NXReadTypes(stream, typeVectorVersion3, typeValuesVersion3);
       backgroundColor = NXReadColor(stream);
       borderType = 0;
       horizontalLayoutType = NX_LEFTALIGNED;
       verticalLayoutType = WW_BOTTOMALIGNED;
    } 
    if (version == 4)
    {  NXReadTypes(stream, typeVectorVersion4, typeValuesVersion4);
       backgroundColor = NXReadColor(stream);
       horizontalLayoutType = NX_LEFTALIGNED;
       verticalLayoutType = WW_BOTTOMALIGNED;
    } 
    if (version == 5)
    {  NXReadTypes(stream, typeVectorVersion5, typeValuesVersion5);
       backgroundColor = NXReadColor(stream);
       verticalLayoutType = WW_BOTTOMALIGNED;
    } 
    if (version == 6)
    {  NXReadTypes(stream, typeVector, typeValues);
       backgroundColor = NXReadColor(stream);
    } 
    return self;
}


- write:(NXTypedStream *)stream 
{
    [super write:stream];
    NXWriteTypes(stream, typeVector, typeValues);
    NXWriteColor(stream, backgroundColor);

    return self;
}

    

@end
